#include "src/net/acceptor.h"

#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>

#include "src/base/fio66_logger.h"
#include "src/net/event_loop.h"
#include "src/net/inet_address.h"
#include "src/net/sockets_opt.h"

namespace fio66::net
{
Acceptor::Acceptor(EventLoop* loop, const InetAddress& ListenAddr, bool reuseport)
  : loop_(loop),
    accept_socket_(sockets::CreateNonblockingOrDie(ListenAddr.Family())),
    accept_channel_(loop, accept_socket_.Fd()),
    listening_(false),
    idle_fd_(::open("/dev/null", O_RDONLY | O_CLOEXEC))
{
  assert(idle_fd_ >= 0);
  accept_socket_.SetReuseAddr(true);
  accept_socket_.SetReusePort(reuseport);
  accept_socket_.BindAddress(ListenAddr);
  accept_channel_.SetReadCallback(
      std::bind(&Acceptor::HandleRead, this));
}

Acceptor::~Acceptor()
{
  accept_channel_.DisableAll();
  accept_channel_.Remove();
  ::close(idle_fd_);
}

void Acceptor::Listen()
{
  loop_->AssertInLoopThread();
  listening_ = true;
  accept_socket_.Listen();
  accept_channel_.EnableReading();
}

void Acceptor::HandleRead()
{
  loop_->AssertInLoopThread();
  InetAddress peerAddr;
  //FIXME loop until no more
  int connfd = accept_socket_.Accept(&peerAddr);
  if (connfd >= 0)
  {
    // string hostport = peerAddr.toIpPort();
    // LOG_TRACE << "Accepts of " << hostport;
    if (new_connection_callback_)
    {
      new_connection_callback_(connfd, peerAddr);
    }
    else
    {
      sockets::Close(connfd);
    }
  }
  else
  {
    LOGE << "in Acceptor::handleRead";
    // Read the section named "The special problem of
    // accept()ing when you can't" in libev's doc.
    // By Marc Lehmann, author of libev.
    if (errno == EMFILE)
    {
      ::close(idle_fd_);
      idle_fd_ = ::accept(accept_socket_.Fd(), NULL, NULL);
      ::close(idle_fd_);
      idle_fd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
    }
  }
}  
  
} // namespace fio66::net
